#include "response.h"
#define module_logFile//启用日志
#ifdef module_logFile
#include "logFile.h"
#endif

const char* RESPONSE_400 = "HTTP/1.1 400 Bad request\r\n\r\n";
const char* RESPONSE_404 = "HTTP/1.1 404 Not Found\r\n\r\n";
const char* RESPONSE_501 = "HTTP/1.1 501 Not Implemented\r\n\r\n";
const char* RESPONSE_505 = "HTTP/1.1 505 HTTP Version not supported\r\n\r\n";


char* path_default = "./static_site";
char* status_line_200 = "HTTP/1.1 200 OK\r\n";
char* server_version = "Server: liso/1.0\r\n";
char* null_line = "\r\n";
#ifdef module_logFile
HttpResponse res;//记录响应信息
#endif
Type_item mime_map[] = {
    //文本类型
    {"html", "text/html"},
    {"htm",  "text/html"},
    {"css",  "text/css"},
    
    //图像类型
    {"png",  "image/png"},
    {"jpg",  "image/jpeg"},
    {"jpeg", "image/jpeg"},
    {"gif",  "image/gif"},
    {"ico",  "image/x-icon"},
    
    //应用类型
    {"pdf",  "application/pdf"},
    {"zip",  "application/zip"},
    {"json", "application/json"},
    {"js",   "application/javascript"},
    
    //多媒体类型
    {"mp3",  "audio/mpeg"},
    {"mp4",  "video/mp4"},
    
    //字体类型
    {"woff",  "font/woff"},
    
    //默认类型
    {"","application/octet-stream"}

};


void get_content_type(char* content_type,char* path){
	int len = strlen(path);
	char ext[64] = {0}; 

	for(int i = len - 1;i >= 0 && len - i < 64;i--){ //从末尾向前找‘.’
		if(path[i] != '.') continue;
        strncpy(ext,path + i + 1,len - 1 - i);
        break;
	}

    if(!strlen(ext)){	
		sprintf(content_type, "Content-Type: application/octet-stream\r\n"); //默认返回二进制流类型
		return;
	}
	
    //把拓展名变成小写
	for(int i = 0;ext[i];i++) ext[i] = tolower(ext[i]);

    for(size_t i = 0;i < sizeof(mime_map) / sizeof(Type_item);i++) {
        if(strcasecmp(ext,mime_map[i].ext) == 0){
            sprintf(content_type,"Content-Type: %s\r\n",mime_map[i].mime);
			return;
        }
    }

    content_type = "Content-Type: application/octet-stream"; 
}


void get_date(char* date){
    time_t now; 
	time(&now); 
	struct tm* t = localtime(&now); 
	strftime(date,256,"Date: %a, %d %b %Y %H:%M:%S %Z\r\n",t);
}


void get_content_length(char* content_length,struct stat* file_status){
    sprintf(content_length,"Content-Length: %zu\r\n",file_status->st_size);
}

char* get_connection(Request* request){
	for(int i = 0;i < request->header_count;i++){
		if(!strcasecmp(request->headers[i].header_name,"Connection")){
            if(!strcasecmp(request->headers[i].header_value,"close"))
                return "Connection: close\r\n";
            return "Connection: keep-alive\r\n";
        }
	}
	return "Connection: keep-alive\r\n"; //除非有要求close否则默认keep-alive
}


void get_last_modified(char* last_modified,struct stat* file_status){
	struct tm *t = gmtime(&file_status->st_mtime);
	strftime(last_modified,256,"Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",t);
}




void get_file_content(Buffer* bf,char* path){
	int fd = 0;
	if((fd = open(path,O_RDONLY))<0){
		printf("Cannot Open file '%s'\n" ,path);
		return;
	}

	struct stat file_stat;
	if((fstat(fd,&file_stat))<0){
		printf("Fetching Status of File '%s' Error\n", path);
		return;
	}

	size_t file_size = file_stat.st_size;
	if(file_size <= 0){
		printf("File '%s' Empty\n" ,path);
		close(fd);
		return;
	}

    char* file_buf = malloc(file_size);
    if(!file_buf){
        printf("file_buf malloc failed\n");
        close(fd);
        return;
    }

    ssize_t bytes_read = read(fd,file_buf,file_size);
    if(bytes_read < 0){
        perror("read file failed\n");
        free(file_buf);
        close(fd);
        return;
    } 
	else if(bytes_read != file_size){
        printf("Partial read (expected %ld, got %zd)\n", file_size,bytes_read);
    }

	buffer_append(bf,file_buf,bytes_read);
	free(file_buf);
	close(fd);
	
}




//检查请求方法是否已实现
METHOD_CLASS checkMethod(Request* request){
    METHOD_CLASS mc = METHOD_UNIMPLEMENT;
    if(!strcasecmp(request->http_method,"GET"))
        mc = METHOD_GET;
    else if(!strcasecmp(request->http_method,"HEAD"))
        mc = METHOD_HEAD;
    else if(!strcasecmp(request->http_method,"POST"))
        mc = METHOD_POST;
    else printf("????  %s\n",request->http_method);
    return mc;
}

char *getHeaderValue(Request *request, char *header_name){
	for(int i = 0;i < request->header_count;i++){
		if(!strcmp(request->headers[i].header_name, header_name))
			return request->headers[i].header_value;
	}
	return "";
}

bool parseRequest(Buffer* bf_recv,struct sockaddr_in* cli_addr,int cli_sock){
    bool isPersistent = true;
    int r = 0;
    printf("bf_recv->data = \n%s\n",bf_recv->data);
    Request* request = parse(bf_recv->data,bf_recv->len,cli_sock);

    char* connection_value = getHeaderValue(request, "Connection");
    if(!strcmp(connection_value, "Close")){
        isPersistent = false;
    }
    
    if(request == NULL){ //格式错误
        printf("Parse request error\n");
        r = send(cli_sock,RESPONSE_400,strlen(RESPONSE_400),0);
        if(r < 0) printf("Send back error\n"); 
        #ifdef module_logFile
        res=resp[1];
        error_log(res,cli_addr);
        #endif
    }
    else if(strcasecmp(request->http_version,"HTTP/1.1")){ //版本错误
        r = send(cli_sock,RESPONSE_505,strlen(RESPONSE_505),0);
        if(r < 0) printf("Send back error\n"); 
        #ifdef module_logFile
        res=resp[4];
        error_log(res,cli_addr);
        #endif
    }
    else{
        METHOD_CLASS mc = checkMethod(request); //请求的方法类型
        switch(mc){
            case METHOD_GET:
                getMethod(request,cli_addr,cli_sock);
                break;
            case METHOD_HEAD:
                headMethod(request,cli_addr,cli_sock);
                break;
            case METHOD_POST:
                postMethod(request,bf_recv,cli_addr,cli_sock);
                break;
            case METHOD_UNIMPLEMENT: //未实现的方法
                r = send(cli_sock,RESPONSE_501,strlen(RESPONSE_501),0);
                if(r < 0) printf("Send back error\n"); 
                #ifdef module_logFile
                res=resp[3];
                error_log(res,cli_addr);
                #endif
                break;
        }
    }
    
    return isPersistent;
}



/*
对get的HTTP响应示例
HTTP/1.1 200 OK\r\n
Server: nginx/1.18.0\r\n
Date: Wed, 21 Oct 2023 07:28:00 GMT\r\n
Content-Type: text/html; charset=UTF-8\r\n
Content-Length: 1256\r\n
Connection: keep-alive\r\n
Last-Modified: Tue, 20 Oct 2023 09:15:22 GMT\r\n
\r\n
<!DOCTYPE html>
<html>...</html>
*/

void getMethod(Request* request,struct sockaddr_in* cli_addr,int cli_sock){
    char* uri = request->http_uri;
    char path[256] = {0}; 

    if(!strcmp(uri,"/")) sprintf(path,"%s/index.html",path_default);
    else sprintf(path,"%s%s",path_default,uri);

    struct stat file_state;
    if(stat(path,&file_state)){ //返回值非零,文件不存在
        int r = send(cli_sock,RESPONSE_404,strlen(RESPONSE_404),0);
        if(r < 0) printf("Send back error\n"); 
        #ifdef module_logFile
        res=resp[2];
        error_log(res,cli_addr);
        #endif
        return;
    }
    
    //获取各种头部字段
    char date[256] = {0};
    char content_type[256] = {0};
    char content_length[256] = {0};
    char* connection;
    char last_modified[256] = {0};

    get_date(date);
    get_content_type(content_type,path);
    get_content_length(content_length,&file_state);
    connection = get_connection(request); //传回只读字符串,不会丢失
    get_last_modified(last_modified,&file_state);
    Buffer bf_send; //响应内容缓冲区
    buffer_init(&bf_send);
    
    buffer_append(&bf_send,status_line_200,strlen(status_line_200)); //状态行
    buffer_append(&bf_send,server_version,strlen(server_version));   //Server
    buffer_append(&bf_send,date,strlen(date));                       //Date
    buffer_append(&bf_send,content_type,strlen(content_type));       //Content-Type
    buffer_append(&bf_send,content_length,strlen(content_length));   //Content-Length
    buffer_append(&bf_send,connection,strlen(connection));           //Connection
    buffer_append(&bf_send,last_modified,strlen(last_modified));     //Last-Modified
    buffer_append(&bf_send,null_line,strlen(null_line));             //空行结束响应头
    get_file_content(&bf_send,path);                                 //响应体
    // printf("1-----------------------------------------\n");
    // printf("bf_send->data = %s\n",bf_send.data);
    // printf("bf_send->len = %zu\n",bf_send.len);
    // printf("cli_sock = %d\n",cli_sock);
    int r = send(cli_sock,bf_send.data,bf_send.len,0);
    // printf("2-----------------------------------------\n");
    if(r < 0) printf("Send back error\n"); 
    #ifdef module_logFile
    res=resp[0];
    access_log(res,cli_addr,request,path,content_length);
    #endif
    buffer_clear(&bf_send);
}

/*
对head的HTTP响应示例
HTTP/1.1 200 OK\r\n
Server: nginx/1.18.0\r\n
Date: Wed, 21 Oct 2023 07:28:00 GMT\r\n
Content-Type: text/html; charset=UTF-8\r\n
Content-Length: 1256\r\n
Connection: keep-alive\r\n
Last-Modified: Tue, 20 Oct 2023 09:15:22 GMT\r\n
\r\n
*/
void headMethod(Request* request,struct sockaddr_in* client_addr,int cli_sock){
    char* uri = request->http_uri;
    char path[256] = {0}; 

    if(!strcmp(uri,"/")) sprintf(path,"%s/index.html",path_default);
    else sprintf(path,"%s%s",path_default,uri);

    struct stat file_state;
    if(stat(path,&file_state)){ //返回值非零,文件不存在
        int r = send(cli_sock,RESPONSE_404,strlen(RESPONSE_404),0);
        if(r < 0) printf("Send back error\n"); 
        #ifdef module_logFile
        res=resp[2];
        error_log(res,client_addr);
        #endif
        return;
    }
    
    //获取各种头部字段
    char date[256] = {0};
    char content_type[256] = {0};
    char content_length[256] = {0};
    char* connection;
    char last_modified[256] = {0};

    get_date(date);
    get_content_type(content_type,path);
    get_content_length(content_length,&file_state);
    connection = get_connection(request); //传回只读字符串,不会丢失
    get_last_modified(last_modified,&file_state);
    
    Buffer bf_send; //响应内容缓冲区
    buffer_init(&bf_send);

    buffer_append(&bf_send,status_line_200,strlen(status_line_200)); //状态行
    buffer_append(&bf_send,server_version,strlen(server_version));   //Server
    buffer_append(&bf_send,date,strlen(date));                       //Date
    buffer_append(&bf_send,content_type,strlen(content_type));       //Content-Type
    buffer_append(&bf_send,content_length,strlen(content_length));   //Content-Length
    buffer_append(&bf_send,connection,strlen(connection));           //Connection
    buffer_append(&bf_send,last_modified,strlen(last_modified));     //Last-Modified
    buffer_append(&bf_send,null_line,strlen(null_line));             //空行结束响应头

    int r = send(cli_sock,bf_send.data,bf_send.len,0);
    if(r < 0) printf("Send back error\n"); 
    #ifdef module_logFile
    res=resp[0];
    access_log(res,client_addr,request,path,content_length);
    #endif
    buffer_clear(&bf_send);
}


void postMethod(Request* request,Buffer* bf_recv,struct sockaddr_in* cli_addr,int cli_sock){//post直接回显
    int r = send(cli_sock,bf_recv->data,bf_recv->len,0);
    if(r < 0) printf("Send back error\n"); 
    #ifdef module_logFile
    res=resp[0];
    access_log(res,cli_addr,request," ","0");
    #endif
}